package Rangel.common.world.entity;

import Rangel.common.world.helper.RangelWorldHelper;
import javolution.util.FastSet;
import math.geom2d.Point2D;
import Rangel.common.RangelConstants;
import Rangel.common.convex.ConvexHull;
import rescuecore2.misc.Pair;
import rescuecore2.standard.entities.Building;
import rescuecore2.standard.entities.Refuge;
import rescuecore2.standard.entities.StandardEntity;
import rescuecore2.worldmodel.EntityID;

import java.awt.*;
import java.io.*;
import java.util.HashSet;
import java.util.Set;

public class BorderEntities {

    RangelWorldHelper world;

    public BorderEntities(RangelWorldHelper world) {
        this.world = world;
    }

    public Set<EntityID> getBordersOf(double scale) {
        String mapName = world.getMapName();
        if (mapName == null) {
            mapName = Long.toString(world.getUniqueMapNumber());
        }
        String filename = RangelConstants.PRECOMPUTE_DIRECTORY + mapName + ".bom";

        if (new File(filename).exists()) {
            try {
                return readBOM(filename);
            } catch (Exception e) {
                System.err.println("Cannot load BOM data!!!!   " + e.getMessage());
            }
        }
        Set<EntityID> allEntities = world.getBuildingIDs();

        return getBordersOf(allEntities, scale);
    }

    private Set<EntityID> readBOM(String fileName) throws IOException {
        Set<EntityID> borderBuildings = new HashSet<>();
        File f = new File(fileName);
        BufferedReader br = new BufferedReader(new FileReader(f));
        String nl;
        while (null != (nl = br.readLine())) {
            String[] xy = nl.split(",");
            int x = Integer.parseInt(xy[0]);
            int y = Integer.parseInt(xy[1]);

            Building b = world.getBuildingInPoint(x, y);
            borderBuildings.add(b.getID());
        }
        br.close();
        if (RangelConstants.DEBUG_FIRE_BRIGADE) {
            System.out.println("Read from file:" + fileName);
        }
        return borderBuildings;
    }

    public Set<EntityID> getBordersOf(Set<EntityID> allEntities, double scale) {
        ConvexHull convexHull = new ConvexHull();

        for (EntityID id : allEntities) {
            Pair<Integer, Integer> location = world.getWorldInfo().getLocation(id);
            convexHull.addPoint(location.first(), location.second());
        }

        return getBorderEntities(convexHull, allEntities, scale);
    }


    public Set<EntityID> getBorderEntities(ConvexHull convex, Set<EntityID> entities, double scale) {
        if (scale >= 1.0) {
            System.err.println("scale should not be over 1.0! check it in border entities, border entities doesn't work now!");
            return null;
        }

        Building building;
        Polygon convexObject = convex.convex();
        Set<EntityID> borderEntities = new FastSet<>();

        if (convexObject.npoints == 0) { // I don't know why this happens, report me if this error writes usually! TODO check this if something comes wrong here
            System.out.println("Something gone wrong in setting border entities for Firebrigade!!!");
            return null;
        }

        Polygon smallBorderPolygon = scalePolygon(convexObject, scale);

        for (EntityID entityID : entities) {

            StandardEntity entity = world.getEntity(entityID);

            if (entity instanceof Refuge)
                continue;
            if (!(entity instanceof Building))
                continue;
            building = (Building) entity;
            int[] vertexes = building.getApexList();
            for (int i = 0; i < vertexes.length; i += 2) {

                if ((convexObject.contains(vertexes[i], vertexes[i + 1])) && !(smallBorderPolygon.contains(vertexes[i], vertexes[i + 1]))) {
                    borderEntities.add(building.getID());
                    break;
                }
            }
        }

        return borderEntities;
    }

    protected Polygon scalePolygon(Polygon sourcePolygon, double scale) {
        Polygon scaledPolygon;

        int[] xs = new int[sourcePolygon.npoints];
        int[] ys = new int[sourcePolygon.npoints];
        Point2D p, p1;

        for (int i = 0; i < sourcePolygon.npoints; i++) {
            p = new Point2D(sourcePolygon.xpoints[i], sourcePolygon.ypoints[i]);
            p1 = p.scale(scale);
            xs[i] = (int) p1.getX();
            ys[i] = (int) p1.getY();
            p.clone();
        }

        Polygon preScaledPolygon = new Polygon(xs, ys, sourcePolygon.npoints);
        scaledPolygon = reAllocatePolygon(preScaledPolygon, sourcePolygon);
        if (scaledPolygon == null)
            scaledPolygon = preScaledPolygon;
        return scaledPolygon;
    }

    protected Polygon reAllocatePolygon(Polygon scaled, Polygon source) {
        if (source == null || scaled == null || source.npoints == 0 || scaled.npoints == 0)
            return null;
        Polygon reAllocated;
        int[] xs = new int[scaled.npoints];
        int[] ys = new int[scaled.npoints];

        int sourceCenterX = 0;
        int sourceCenterY = 0;

        int scaledCenterX = 0;
        int scaledCenterY = 0;

        for (int i = 0; i < scaled.npoints; i++) {
            sourceCenterX += source.xpoints[i];
            sourceCenterY += source.ypoints[i];

            scaledCenterX += scaled.xpoints[i];
            scaledCenterY += scaled.ypoints[i];
        }

        sourceCenterX = sourceCenterX / source.npoints;
        sourceCenterY = sourceCenterY / source.npoints;

        scaledCenterX = scaledCenterX / scaled.npoints;
        scaledCenterY = scaledCenterY / scaled.npoints;

        int xDistance = sourceCenterX - scaledCenterX;
        int yDistance = sourceCenterY - scaledCenterY;

        for (int i = 0; i < scaled.npoints; i++) {
            xs[i] = scaled.xpoints[i] + xDistance;
            ys[i] = scaled.ypoints[i] + yDistance;
        }
        reAllocated = new Polygon(xs, ys, scaled.npoints);

        return reAllocated;
    }

}